using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
using RolePlayingGameData;
using System.IO;
namespace WindowsGame2
{
    /// <summary>
    /// This is the main type for your game
    /// </summary>
    public class Game1 : Microsoft.Xna.Framework.Game
    {
        GraphicsDeviceManager m_graphics;
        SpriteBatch m_spriteBatch;
        WindowsGame2.Map.MapObject m_mapObject = new WindowsGame2.Map.MapObject();
        Vector2 m_mapOriginPosition = Vector2.Zero;
        Viewport m_viewport;

        enum LBUTTON_MOUSE_COMMAND_MODE
        {
            SELECT_CHARACTER,
            TARGET_CHARACTER,
        }

        LBUTTON_MOUSE_COMMAND_MODE m_lbutton_mouse_command_mode = LBUTTON_MOUSE_COMMAND_MODE.SELECT_CHARACTER;

        List<WindowsGame2.Characters.PlayerObject> m_playerContainer = new List<WindowsGame2.Characters.PlayerObject>();
        WindowsGame2.Characters.PlayerObject m_selectedPlayerObject = null;
        WindowsGame2.Characters.PlayerObject m_targetedPlayerObject = null;

        public Game1()
        {
            m_graphics = new GraphicsDeviceManager(this);
            Content.RootDirectory = "Content";
        }

        /// <summary>
        /// Allows the game to perform any initialization it needs to before starting to run.
        /// This is where it can query for any required services and load any non-graphic
        /// related content.  Calling base.Initialize will enumerate through any components
        /// and initialize them as well.
        /// </summary>
        protected override void Initialize()
        {
            // TODO: Add your initialization logic here
            InputManager.Initialize();
            m_mapObject.Initialize(Content, m_graphics.GraphicsDevice.Viewport);
            m_viewport = m_graphics.GraphicsDevice.Viewport;
            this.IsMouseVisible = true;
            base.Initialize();
        }

        /// <summary>
        /// LoadContent will be called once per game and is the place to load
        /// all of your content.
        /// </summary>
        protected override void LoadContent()
        {
            // Create a new SpriteBatch, which can be used to draw textures.
            m_spriteBatch = new SpriteBatch(GraphicsDevice);

            // TODO: use this.Content to load your game content here
            GameStartDescription gameStartDescription = Content.Load<GameStartDescription>("MainGameDescription");
            m_mapObject.LoadContent(gameStartDescription.MapContentName);            
            
            foreach(string contentName in gameStartDescription.PlayerContentNames)
            {
                WindowsGame2.Characters.PlayerObject player = new WindowsGame2.Characters.PlayerObject();
                player.Initialize(Content, m_viewport, m_mapObject.MapData.TileSize);
                player.LoadContent(contentName, new Point(m_mapObject.MapData.SpawnMapPosition.X+m_playerContainer.Count
                                                        , m_mapObject.MapData.SpawnMapPosition.Y));

                m_playerContainer.Add(player);
            }
        }

        /// <summary>
        /// UnloadContent will be called once per game and is the place to unload
        /// all content.
        /// </summary>
        protected override void UnloadContent()
        {
            // TODO: Unload any non ContentManager content here
        }

        /// <summary>
        /// Allows the game to run logic such as updating the world,
        /// checking for collisions, gathering input, and playing audio.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        protected override void Update(GameTime gameTime)
        {
            // Allows the game to exit
            if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)
                this.Exit();

            InputManager.Update();
            UpdateMapObject();
            UpdateScrollingView();
            UpdatePlayer((float)gameTime.ElapsedGameTime.TotalSeconds);
            // TODO: Add your update logic here
            
            base.Update(gameTime);
        }

        void UpdateMapObject()
        {
            m_mapObject.Update();
        }

        void UpdateScrollingView()
        {
            if (InputManager.IsActionPressed(InputManager.Action.MoveCharacterLeft))
            {
                m_mapOriginPosition.X = Math.Min(m_mapOriginPosition.X + 10, 0);
            }
            if (InputManager.IsActionPressed(InputManager.Action.MoveCharacterRight))
            {
                m_mapOriginPosition.X = Math.Max(m_mapOriginPosition.X - 10, m_viewport.Width - m_mapObject.Boundary.X);
            }
            if (InputManager.IsActionPressed(InputManager.Action.MoveCharacterUp))
            {
                m_mapOriginPosition.Y = Math.Min(m_mapOriginPosition.Y + 10, 0);
            }
            if (InputManager.IsActionPressed(InputManager.Action.MoveCharacterDown))
            {
                m_mapOriginPosition.Y = Math.Max(m_mapOriginPosition.Y - 10, m_viewport.Height - m_mapObject.Boundary.Y);
            }
        }

        public void UpdatePlayer(float elapsedGameTimeInSeconds)
        {
            foreach (WindowsGame2.Characters.PlayerObject playerObject in m_playerContainer)
            {
                m_mapObject.OccupiedTilePositionByDynamicObject(playerObject.TilePosition, playerObject.Sight);
            }

            if (InputManager.IsKeyPressed(Keys.A))
            {
                m_lbutton_mouse_command_mode = LBUTTON_MOUSE_COMMAND_MODE.TARGET_CHARACTER;
            }

            Vector2 mousePos;
            if (InputManager.IsMouseLButonPressed(out mousePos))
            {
                foreach (WindowsGame2.Characters.PlayerObject playerObject in m_playerContainer)
                {
                    if (playerObject.IsInPoint(mousePos - m_mapOriginPosition))
                    {
                        switch (m_lbutton_mouse_command_mode)
                        {
                            case LBUTTON_MOUSE_COMMAND_MODE.SELECT_CHARACTER:
                                {
                                    if (m_selectedPlayerObject != null)
                                    {
                                        m_selectedPlayerObject.IsSelected = false;
                                    }

                                    playerObject.IsSelected = true;
                                    m_selectedPlayerObject = playerObject;
                                }
                                break;
                            case LBUTTON_MOUSE_COMMAND_MODE.TARGET_CHARACTER:
                                {
                                    m_selectedPlayerObject.Attack(ref m_mapOriginPosition, playerObject);
                                    m_targetedPlayerObject = playerObject;
                                }
                                break;
                        }
                        break;
                    }
                }

                m_lbutton_mouse_command_mode = LBUTTON_MOUSE_COMMAND_MODE.SELECT_CHARACTER;
                        
            }
            else if (InputManager.IsMouseRButonPressed(out mousePos))
            {
                if (m_selectedPlayerObject == null) return;

                Point desiredTilePosition = m_selectedPlayerObject.TilePosition;
                Vector2 desiredTileOffset = mousePos - m_mapOriginPosition;               
                
                desiredTilePosition.X = (int)desiredTileOffset.X / m_mapObject.MapData.TileSize.X;
                desiredTilePosition.Y = (int)desiredTileOffset.Y / m_mapObject.MapData.TileSize.Y;
                bool canWalk = m_mapObject.DoPathFinding(m_selectedPlayerObject.TilePosition, desiredTilePosition, out m_selectedPlayerObject.m_waypointContainer);

                if (canWalk)
                {
                    m_selectedPlayerObject.DesiredMovement = Vector2.Multiply(
                           new Vector2(m_mapObject.MapData.TileSize.X, m_mapObject.MapData.TileSize.Y)
                         , new Vector2(m_selectedPlayerObject.m_waypointContainer[0].m_x - m_selectedPlayerObject.TilePosition.X
                                     , m_selectedPlayerObject.m_waypointContainer[0].m_y - m_selectedPlayerObject.TilePosition.Y)) - (m_selectedPlayerObject.TileOffset);

                    Console.WriteLine("AStart path start({0}, {1})", m_selectedPlayerObject.TilePosition.X, m_selectedPlayerObject.TilePosition.Y);
                    foreach (WindowsGame2.Map.TILE_POSITION tilePosition in m_selectedPlayerObject.m_waypointContainer)
                    {
                        Console.WriteLine("tilePosition ({0}, {1})", tilePosition.m_x, tilePosition.m_y);
                    }
                    m_selectedPlayerObject.m_waypointContainer.RemoveAt(0);
                }
            }

            foreach (WindowsGame2.Characters.PlayerObject playerObject in m_playerContainer)
            {
                playerObject.Update(m_mapObject, elapsedGameTimeInSeconds);                
            }
        }

        /// <summary>
        /// This is called when the game should draw itself.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        protected override void Draw(GameTime gameTime)
        {
            GraphicsDevice.Clear(Color.CornflowerBlue);

            // TODO: Add your drawing code here
            m_spriteBatch.Begin();
            m_mapObject.DrawBackground(m_spriteBatch, m_mapOriginPosition);
            m_spriteBatch.End();

            m_spriteBatch.Begin(SpriteSortMode.BackToFront, BlendState.AlphaBlend);            
            foreach (WindowsGame2.Characters.PlayerObject playerObject in m_playerContainer)
            {
                playerObject.Draw(m_spriteBatch, (float)gameTime.ElapsedGameTime.TotalSeconds, m_mapOriginPosition);
                m_mapObject.DrawMapObject(m_spriteBatch, m_mapOriginPosition);
            }            
            m_spriteBatch.End();

            m_spriteBatch.Begin();
            m_mapObject.DrawFog(m_spriteBatch, m_mapOriginPosition);
            m_spriteBatch.End();

            base.Draw(gameTime);
        }

        void DrawGrid()
        {

            Texture2D tex = new Texture2D(m_graphics.GraphicsDevice, m_mapObject.MapData.Texture.Width, m_mapObject.MapData.Texture.Height, true, SurfaceFormat.Color);
            Color[] colors = new Color[m_mapObject.MapData.Texture.Width * m_mapObject.MapData.Texture.Height];
            for (int y = 0; y < m_mapObject.MapData.Texture.Height; y += m_mapObject.MapData.TileSize.Y)
            {
                for (int x = 0; x < m_mapObject.MapData.Texture.Width; x += m_mapObject.MapData.TileSize.X)
                {
                    colors[x + y * m_mapObject.MapData.Texture.Width] = Color.White;
                }

            }
            tex.SetData<Color>(colors);
            m_spriteBatch.Draw(tex, m_mapOriginPosition, Color.White);
        }
    }
}
